import java.net.*;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.io.*;

public class Connection extends Thread {
	private Peer peer;
	private int port, bytesRead ;
	private Communicator myComm;
	private int percentage;
	//private ProjectManager myProject;
	private boolean isRefresh;
	private static boolean sendpeerbool = true;
	public String fileNameHolder = null;
	public int fileSizeHolder = -1;
	//private ObjectOutputStream oops;
	//private ObjectInputStream oips;
	//private InputStream  is;
	//private File file;

	private Socket mySocket;

	public Connection(Socket s) {
		System.out.println(this);
		System.out.println("for the sake of fuck sake");
		mySocket = s;
		//		try {
		//			mySocket.setTcpNoDelay(false);
		//		} catch (SocketException e) {
		//			// TODO Auto-generated catch block
		//			e.printStackTrace();
		//		}
		setPort(mySocket.getPort());
		myComm = new Communicator();
		setRefresh(false);
		myComm.sendMessage((char) PC.GET_PEERS);
		/*try {
			ObjectInputStream readInput = new ObjectInputStream(mySocket.getInputStream());
			ConcurrentHashMap readPeerFileMap = (ConcurrentHashMap) readInput.readObject();
			ProjectManager.peerToFileMap.putAll(readPeerFileMap);
		} catch (IOException e) {}
		catch (ClassNotFoundException e) {}*/
	}

	public void run() { //as its currently structured, will only allow one transfer at a time?
		System.out.println("Connection ActiveConnection:" + GUI1.myProject.activeConnections.keySet());
		try {
			while (GUI1.myProject.isRunning() && !mySocket.isClosed()) {
				//System.out.println("before");
				if(fileNameHolder != null && fileSizeHolder >= 0){
					String fnh = fileNameHolder;
					int fsh = fileSizeHolder;
					fileNameHolder = null;
					fileSizeHolder = -1;
					getFile(fnh, fsh);
				}
				myComm.interpretMessage();
				//System.out.println("after");
				Thread.sleep(1);
			}
			if (!mySocket.isClosed()) {
				mySocket.close();
			}
			checkSocket();
		} catch (IOException e) {} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void sendFile(String filename) throws IOException{
		File file = new File(filename);
		long fileSize = file.length();
		System.out.println(fileSize);
		byte [] bytesArray  = new byte [(int) fileSize];
		FileInputStream fis = new FileInputStream(file);
		BufferedInputStream bis = new BufferedInputStream(fis);
		bis.read(bytesArray,0,bytesArray.length);
		OutputStream os = mySocket.getOutputStream();
		System.out.println("Sending the file...");
		os.write(bytesArray,0,bytesArray.length);
		os.flush();
		//os.close();

	}

	public void getFile(String filename, int fileSize) throws IOException, ClassNotFoundException{
		myComm.sendMessage((char)PC.GET_FILE);
		myComm.sendMessage(filename);
		byte [] bytesArray   = new byte[fileSize];
		InputStream is = mySocket.getInputStream();
		System.out.println("receiving the requested file...");
		FileOutputStream fos = new FileOutputStream(filename);
		BufferedOutputStream bos = new BufferedOutputStream(fos);
		bytesRead = is.read(bytesArray,0,bytesArray.length);
		int current = bytesRead;

		do {
			bytesRead =
				is.read(bytesArray, current, (bytesArray.length-current));
			if(bytesRead >= 0) current += bytesRead;
			if(current!=0)
			{
				percentage  =  0;}
			else 
			{
				percentage  =  current/bytesArray.length;}

		} while(bytesRead > -1  && current != fileSize);

		bos.write(bytesArray, 0 , current);
		bos.flush();
		//long end = System.currentTimeMillis();
		//System.out.println(end-start);
		//bos.close();		
	}
	public int download(){
		return percentage;
	}
	public void sendPeers() {

		try {
			//ObjectOutputStream sender = new ObjectOutputStream(mySocket.getOutputStream());
			CopyOnWriteArraySet<Peer> toPass = new CopyOnWriteArraySet<Peer>();
			toPass.addAll(GUI1.myProject.activeConnections.keySet());
			myComm.talker.writeObject(toPass);
			myComm.talker.flush();
			//sender.close();
			if (mySocket.isClosed()) {
				System.out.println("afdsafsd");
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		myComm.sendMessage((char) PC.SEND_FILE_LIST);
		sendFileList();

	}

	public void sendFileList() {
		//ObjectOutputStream sender;
		try {
			//sender = new ObjectOutputStream(mySocket.getOutputStream());
			myComm.talker.writeObject(GUI1.myProject.localFileList);
			myComm.talker.flush();
			//sender.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@SuppressWarnings("unchecked")
	public void checkPeersInput() {
		System.out.println(this);

		if(sendpeerbool){
			/*try {
		ObjectInputStream readInput = new ObjectInputStream(mySocket.getInputStream());
		ConcurrentHashMap readPeerFileMap = (ConcurrentHashMap) readInput.readObject();
		ProjectManager.peerToFileMap.putAll(readPeerFileMap);
		} */
			try {
				System.out.println("betfore");
				System.out.println(mySocket.getInputStream().available());
				//ObjectInputStream readInput = new ObjectInputStream(mySocket.getInputStream());
				System.out.println("before");
				CopyOnWriteArraySet<Peer> readPeerSet = (CopyOnWriteArraySet<Peer>) myComm.listener.readObject();
				System.out.println("after");
				addConnections(readPeerSet);
			} catch (IOException e) {
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
			sendpeerbool = false;
		}
	}

	@SuppressWarnings("unchecked")
	public void checkFileListInput() {
		try {
			//ObjectInputStream readInput = new ObjectInputStream(mySocket.getInputStream());
			CopyOnWriteArraySet<FileDetails> readFileList = (CopyOnWriteArraySet<FileDetails>) myComm.listener.readObject();
			GUI1.myProject.peerToFileMap.putIfAbsent(new Peer(mySocket.getInetAddress()), readFileList);
			System.out.println(readFileList.toString());

			for(FileDetails fd: readFileList) {
				if (GUI1.myProject.fileToPeerMap.containsKey(fd)) {
					GUI1.myProject.fileToPeerMap.get(fd).add(new Peer(mySocket.getInetAddress()));
				} else {
					CopyOnWriteArraySet<Peer> getThing = GUI1.myProject.fileToPeerMap.get(fd);
					CopyOnWriteArraySet<Peer> newThing = new CopyOnWriteArraySet();
					newThing.add(new Peer(mySocket.getInetAddress()));
					GUI1.myProject.fileToPeerMap.putIfAbsent(fd, newThing);
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}

	public void addConnections(Set<Peer> peerSet) {
		for (Peer p:peerSet) {
			if (!GUI1.myProject.activeConnections.containsKey(p)) {
				GUI1.myProject.connectTo(p, GUI1.myProject.getCurrentPort());
			}
		}
	}

	public void checkSocket() {
		if (mySocket.isClosed()) {
			removeConnection();
		}
	}

	public void removeConnection() {
		GUI1.myProject.activeConnections.remove(peer);
		GUI1.myProject.peerToFileMap.remove(peer);
	}

	public void setRefresh(){
		isRefresh = true;
	}

	public void doRefresh(){

		setRefresh(false);
	}

	/*public static void main(String[] args) {

		System.out.println("test code: server running....");
		ServerSocket ss = null;
		Socket s = null;
		try {
			ss = new ServerSocket(8888);
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		while ( s == null ) {
		try {
			s = ss.accept();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		}
		Connection testConnection = new Connection(s);
		testConnection.start();

		while(s.isConnected()) {}
	}*/

	public void setRefresh(boolean isRefresh) {
		this.isRefresh = isRefresh;
	}

	public boolean isRefresh() {
		return isRefresh;
	}

	public void setPort(int port) {
		this.port = port;
	}

	public int getPort() {
		return port;
	}

	private class Communicator{

		public ObjectOutputStream talker;
		public ObjectInputStream listener;

		public char currentMessage;

		public Communicator() {
			try {
				talker = new ObjectOutputStream(mySocket.getOutputStream());

				listener = new ObjectInputStream(mySocket.getInputStream());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		public void sendMessage(String message) {
			System.out.println("sendMessage: " + message);
			try {
				talker.writeObject(message + '\n');
				talker.flush();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		public void sendMessage(char message) {
			System.out.println("sendMessage: " + message);
			try {
				talker.writeChar(message);
				talker.flush();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		public void interpretMessage() {
			currentMessage = 0;
			try {
				if (listener.available() >= 1) {
					currentMessage = (char) listener.readChar();

					if (currentMessage == (char) PC.GET_FILE) {
						String cM;
						try {
							cM = (String) listener.readObject();
							cM = cM.trim();

							doGetFile(cM);
						} catch (ClassNotFoundException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}

					} 
					else if (currentMessage == (char) PC.GET_PEERS) {

						doGetPeers();
					} 
					else if (currentMessage == (char) PC.SEND_PEERS) {
						System.out.println("ASDF" + this.toString());
						doSendPeers();
					} 
					else if (currentMessage == (char) PC.SEND_FILE_LIST) {
						doSendFileList();
					}
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		//GetFile means that the other computer requested said file
		//send means that the other other computer just sent something
		public void doGetFile(String cM) {
			//String[] currentMessageParsed = cM.split(",");
			try {
				sendFile(GUI1.myProject.getCwd() + "/" + cM);
			} catch (IOException e) {}
		}

		public void doGetPeers() {
			System.out.println("about to do get peers" + currentMessage);

			sendMessage((char) PC.SEND_PEERS); //do I need the \n? ---------------------------------------
			sendPeers();
		}

		public void doSendPeers() {
			if (sendpeerbool) {
				//message = listener.readLine();
				System.out.println("about to do send peers" + currentMessage);
				checkPeersInput();
				System.out.println(GUI1.myProject.peerToFileMap.keySet());
			}
		}

		public void doSendFileList() {
			System.out.println("about to do send file list" + currentMessage);

			checkFileListInput();
		}
	}
}


/*
 * 
 * else if (message.matches("getPeerList")) {
						sendPeerList();
					} else if (message.matches("getFileList")) {
						sendFileList();
					}
 */
/*public FileList getFileList(){

return null;
}
public  Peer getPeers(){

return null;
}*/

/*public void sendPeerList(){

}

public void sendFileList(){

}*/

